package main

import (
	"errors"
	"fmt"
	"math"
	"os"
	"reflect"
	"runtime"
	"runtime/debug"
	"time"
	// "path/filepath"
	// "strings"
	// "regexp"

	"github.com/rs/zerolog"
	"go.uber.org/zap"

	"gitee.com/irontec/goutil/logger"
	"gitee.com/irontec/goutil/test"
	"test_go_introduction/sdb"
)

func test1() {
	fmt.Println(" ==== test1")

	fmt.Println(fmt.Print("test", ":", 1, 2, "+", 3, "\n"))
	fmt.Println(fmt.Printf("test %d %d %d\n", 1, 2, 3))
	fmt.Println(fmt.Println("math:", math.Exp2(10)))

	if err := percent(20, 40, 70, 100, 120); err != nil {
		fmt.Println(err)
	}
}

func percent(i ...int) error {
	for _, n := range i {
		if n > 100 {
			return fmt.Errorf("Number %d out of range(100)", n)
		}
		fmt.Print(n, "%\n")
	}
	return nil
}

const (
	Type_1 = iota * 2.16
	Type_2
	Type_3
)

func test2() string {
	fmt.Println(" ==== test2")

	var a, b int = 12, 34
	var c string
	fmt.Println(a, b, c)
	fmt.Printf("%#02x, %4.4x, %q\n", a, b, c)

	d := true
	fmt.Println(d)

	e := 1
	fmt.Println(e)
	fmt.Println(&e)

	fmt.Println(Type_1)
	fmt.Println(Type_2)
	fmt.Println(Type_3)

	fmt.Println("test_return:", test_return(4))

	return "test"
}

func test_return(a int) string {
	if a > 3 {
		return "true"
	}
	return "false"
}

func test3() {
	fmt.Println(" ==== test3")
	for i, v := range "123test" {
		fmt.Println(i, v, fmt.Sprintf("%#02x", v), string(v))
	}

	add_func := add(1, 2)
	fmt.Println(add_func(0, 0))
	fmt.Println(add_func(1, 2))
	fmt.Println(add_func(3, 2))
}

func add(x1, x2 int) func(x3, x4 int) (int, int, int) {
	i := 0
	return func(x3, x4 int) (int, int, int) {
		i++
		return i, x1 + x2, x3 + x4
	}
}

func test4() {
	fmt.Println(" ==== test4")
	n := [][]int{
		{1, 2, 3, 4},
		{3, 2, 1, 0},
		{5, 7, 5, 8},
	}
	get_array(n)
	fmt.Println("n:", n)
	xl := len(n) - 1
	yl := len(n[xl]) - 1
	fmt.Println("n[last][last]:", n[xl][yl])

	var p []int // is nil
	// p := []int{} // is []
	fmt.Println("p:", p, p == nil)
}

func get_array(n [][]int) {
	p := n[2:]
	fmt.Println(p)
	fmt.Println(len(p), cap(p))

	n[0][0] = 9
	n = append(n, []int{5, 2, 6, 7, 8}, []int{})
	q := [][]int{{1}, {2}, {}}
	r := append(q[1], 12, 2)
	fmt.Println(r)
	fmt.Println(n)
	fmt.Println(len(n), cap(n))
	fmt.Println(len(n[1]))
}

func test5() {
	fmt.Println(" ==== test5")
	for i, arg := range os.Args {
		fmt.Println(i, arg)
	}
}

type Sdb struct {
}

func (s Sdb) Menu(items []sdb.Item) {
	fmt.Println(items)
}

func test6() {
	defer fmt.Println("test6 finished!")
	fmt.Println(" ==== test6")

	p := []sdb.Item{
		{Name: "----", Param: 0, Func: nil},
	}

	// T1
	s := Sdb{}
	s.Menu(p)
	// T2
	(Sdb{}).Menu(p)
	// T3
	new(Sdb).Menu(p)
	// T4
	var t Sdb
	t.Menu(p)

	sdb.Menu(p)
	sdb.TestSdb()
}

func get_error(a string) (err error) {
	if a == "" {
		return errors.New("Empty input!")
	} else {
		return
	}
}

func test7() {
	defer func() {
		r := recover()
		fmt.Println("got recover:", r)
		fmt.Println("reflect.TypeOf(r):", reflect.TypeOf(r))
		// 反射判断类型
		if reflect.TypeOf(r) == reflect.TypeOf((*sdb.Exception)(nil)).Elem() {
			fmt.Println("check is type of sdb.Exception")
		}
		for i := 0; i < 8; i++ {
			pc, file, line, ok := runtime.Caller(i)
			fmt.Printf("%s; ", runtime.FuncForPC(pc).Name())
			f, l := runtime.FuncForPC(pc).FileLine(pc)
			fmt.Printf("%s, %d; ", f, l)
			fmt.Printf("%d, %s, %d, %s\n", pc, file, line, ok)
			// f, l := runtime.FuncForPC(2).FileLine(2)
			// fmt.Printf("%s:%d\n", f, l)
		}
		debug.PrintStack()

		switch r.(type) {
		case nil:
			fmt.Println("Finished.")
		case sdb.Exception:
			fmt.Println(" => is type of sdb.Exception")
		case error:
			fmt.Println(" => is type of error")
		default:
			fmt.Println(" => unknown type", r)
			panic(r)
		}
	}()

	fmt.Println(" ==== test7")

	// panic(fmt.Errorf("Errorf test"))
	// panic(errors.New("panic test"))
	// panic("panic string")
	panic(sdb.Exception{
		Code:    200,
		Message: "test",
		Post:    nil,
	})
	if ret := get_error(""); ret != nil {
		panic(ret)
	}

	n1 := 10
	n2 := 0
	res := n1 / n2
	fmt.Printf("res=%d/%d=%d\n", n1, n2, res)
}

func test8() {
	defer fmt.Println("finished.")

	fmt.Println(" ==== test8")
	time.Sleep(340 * time.Millisecond)

	ch := make(chan int, 3)
	go func(count int, ch chan<- int) {
		// fmt.Println("send thread started, waiting...")
		// time.Sleep(4 * time.Second)
		fmt.Println("start send.")
		for i := 0; i < count; i++ {
			ch <- i
			fmt.Println("send to channel:", i)
		}
		fmt.Println("finished. waiting...")
		time.Sleep(4 * time.Second)
		close(ch)
		fmt.Println("close channel.")
	}(10, ch)

	fmt.Println("waiting for receive...")
	time.Sleep(4 * time.Second)
	fmt.Println("start receive.")
	for v := range ch {
		fmt.Println("receive value:", v)
		time.Sleep(300 * time.Millisecond)
	}
}

func test9() {
	fmt.Println(" ==== test9")

	zerolog.TimeFieldFormat = time.RFC3339Nano
	io := zerolog.ConsoleWriter{
		Out: os.Stdout,
		// TimeFormat: "20060102 150405.000000000",
		TimeFormat: "150405.000",
		// NoColor: true,
	}
	log := zerolog.New(io)
	log = log.With().Timestamp().Logger()
	log = log.With().Caller().Logger()

	log.Print("print")
	log.Printf("format: %d", 123)
	log.Debug().Msg("debug")
	log.Info().Msg("info")
	log.Warn().Msg("warn")
	log.Error().Msg("error")
	// log.Fatal().Msg("fatal")
	// log.Panic().Msg("panic")

	cwd, err := os.Getwd()
	if err != nil {
		log.Error().Msg(err.Error())
	}
	log.Info().Msgf("Getwd: %v", cwd)
	dir := os.Getenv("PWD")
	log.Info().Msgf("PWD:%v", dir)
	home := os.Getenv("HOME")
	log.Info().Msgf("HOME:%v", home)

	ver := runtime.Version()
	log.Info().Msgf("test: %v", ver)

}

func test10() { // disused
	// logger, _ := zap.NewProduction()
	logger, _ := zap.NewDevelopment()
	defer logger.Sync() // flushes buffer, if any
	sugar := logger.Sugar()
	sugar.Info("hello zap")
	sugar.Infof("zap format output: %s", "test123")
	sugar.Infof("zap format output nothing")
}

func test11() {
	fmt.Println("call test.A()")
	test.A()
	fmt.Println("done")

	var log = logger.Init()
	log.SetLevel(logger.DebugLevel)

	defer log.Info("defer")

	log.Debug("Debug")
	log.Info("Info")
	log.Info("test:%d", 123)
	log.Warn("Warn")
	log.Error("Error")
}

func main() {
	// test1()
	// test2()
	// test3()
	// test4()
	// test5()
	// test6()
	// test7()
	// test8()
	// test9()
	// test10()
	test11()
	defer func() {
		fmt.Println("[END]")
		// time.Sleep(20 * time.Second)
	}()
}
